ci: switch from SLSA provenance to actions/attest with subject-path#377
ci: switch from SLSA provenance to actions/attest with subject-path#377
Conversation
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
Since actions/attest@v4 stores attestations via GitHub's attestation API (not as release assets), repos that only use attestation don't need draft releases. Release-please can publish the release directly. Changes: - Remove draft:true from release-please-config.json - Remove create-tag job/steps (force-tag-creation handles this) - Remove publish-release job (release is published directly) - Remove publish_release input from manual workflows
force-tag-creation only operates in conjunction with draft releases. Since this repo does not use draft releases (attestation-only, no artifact uploads to the release), force-tag-creation is not needed.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
| permissions: | ||
| id-token: write # Needed if using OIDC to get release secrets. | ||
| contents: write # Contents and pull-requests are for release-please to make releases. | ||
| attestations: write # Needed for actions/attest. |
There was a problem hiding this comment.
Missing artifact-metadata: write permission for actions/attest@v4
Medium Severity
Both workflows declare id-token: write and attestations: write for actions/attest@v4, but are missing the artifact-metadata: write permission. Since January 2026, GitHub requires this fine-grained permission for creating artifact storage records, and the contents: write fallback was deprecated in February 2026. Without it, the "Attest build provenance" step will likely fail with a permissions error, resulting in releases without attestation.


Requirements
N/A — CI-only and documentation changes, no application code or tests affected.
Related issues
Supports the org-wide migration to immutable GitHub releases. Once immutable releases are enabled, artifacts can no longer be uploaded after a release is published. This repo only uses attestation (no binary/artifact uploads to the release), so
actions/attest@v4— which stores attestations via GitHub's attestation API rather than as release assets — is compatible with immutable releases without needing draft releases.Describe the solution you've provided
Changes across six files:
release-please-config.json— Reformattedextra-filesarray to multi-line (no functional change)..github/actions/publish/action.yml— Removed thegem-hashoutput and "Hash gem for provenance" step. These existed to produce base64-encoded checksums for the old SLSA generator and are no longer needed since attestation now usessubject-pathdirectly..github/workflows/release-please.yml— In thepublishjob:release-provenancejob (SLSAgenerator_generic_slsa3reusable workflow withupload-assets: true) with an inlineactions/attest@v4step usingsubject-path: 'launchdarkly-server-sdk-*.gem'.attestations: writepermission.gem-hashfrom job outputs.upload-tag-nameoutput (was only consumed by the now-removedrelease-provenancejob). Therelease-createdoutput is retained as it is still used by downstream jobs..github/workflows/manual-publish.yml— Same attestation migration:release-provenancejob with an inlineactions/attest@v4step usingsubject-pathgated on dry_run check.attestations: writepermission.gem-hashfrom job outputs.dry_runconditions updated to useformat('{0}', inputs.dry_run) == 'false'instead of!inputs.dry_run(see note below).PROVENANCE.md— Rewritten to document the newgh attestation verifyworkflow instead of the oldslsa-verifierapproach. Includes updated verification commands and sample output matching the new GitHub attestation API format.README.md— Minor whitespace cleanup in the provenance section; the original SLSA framework text is preserved verbatim with a link toPROVENANCE.mdfor GitHub-specific details.Why
subject-pathinstead ofsubject-checksums?The previous approach used a base64 encode/decode round-trip inherited from the old SLSA generator: the composite action hashed artifacts and base64-encoded the checksums, then the workflow decoded them into a checksums file for
actions/attest. Sinceactions/attest@v4supportssubject-path— which accepts file globs and computes checksums internally — the entire round-trip is eliminated. The globlaunchdarkly-server-sdk-*.gemmatches the built gem files directly on disk.Why no draft releases?
The old SLSA generator uploaded
.intoto.jsonlprovenance files as release assets (viaupload-assets: true), which would fail under immutable releases. The newactions/attest@v4stores attestations via GitHub's attestation API instead — it does not modify the GitHub release. Since this repo has no other artifact uploads, the release can be published directly by release-please without a draft→publish flow.Why
format('{0}', inputs.dry_run)for dry_run checks?GitHub Actions passes
workflow_dispatchboolean inputs as strings ('true'/'false'), butworkflow_callpasses them as actual booleans. The expression!inputs.dry_runsilently coerces differently depending on the trigger type. Usingformat('{0}', inputs.dry_run) == 'false'normalizes both cases to a string comparison, ensuring the condition works regardless of how the workflow is invoked.Describe alternatives you've considered
subject-checksums: An earlier revision decoded base64 hashes into a checksums file foractions/attest. This worked but was unnecessarily complex since the artifact files are already on disk in the same job.publish-releasejob. Simplified since this repo only uses attestation, not artifact uploads.actions/attest@v4is the org-standard replacement and runs inline without requiring a separate job.fromJSON(inputs.dry_run)for boolean coercion: Undocumented behavior when called on an already-boolean value.format()is fully documented for both types.Additional context
dry_rungating correctness: Theformat('{0}', inputs.dry_run) == 'false'pattern is used inmanual-publish.ymlfor the attestation step, secrets retrieval, and docs publishing. Verify that this evaluates correctly for bothworkflow_dispatch(string) andworkflow_call(boolean) triggers in your environment.subject-path: 'launchdarkly-server-sdk-*.gem'glob must match all gem files produced by the build (both CRuby and JRuby variants). The existing publish action already uses the samelaunchdarkly-server-sdk-*.gemglob forgem push, so this should be consistent.ifguard on attestation inrelease-please.yml: TheAttest build provenancestep runs unconditionally within thepublishjob (which is itself gated onrelease-created == 'true'). The manual workflow correctly gates on the dry_run check. Confirm this asymmetry is acceptable.SDK_VERSION=8.13.0inPROVENANCE.mdis managed by release-please viaextra-files. Verify that release-please will update this value on the next release.actions/attest@v4is referenced by major version tag (not pinned SHA), consistent with existingactions/checkout@v4usage in this repo.Link to Devin session: https://app.devin.ai/sessions/7d5bda4d9dbe4ae0b950b30a50485e60
Requested by: @keelerm84